﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

using Palsta.Core.manifest;
using Palsta.Core.data;
using Palsta.Core.view;
using Palsta.Core.action;
using Palsta.Core.exp;
using Palsta.Core.parser;

namespace Palsta.Core
{
    public class InfoPathTranslater
    {
        public Boolean Translate(String path, String outputPath)
        {
            if (!path.EndsWith("\\"))
            {
                path = path + "\\";
            }

            if (!outputPath.EndsWith("\\"))
            {
                outputPath = outputPath + "\\";
            }
            InfoPathFormParser formParser = new InfoPathFormParser();
            InfoPathForm form = formParser.Parse(path);
            if (form == null)
            {
                return false;
            }

            InfoPathPage page = this.TranslateFormToPage(form);
            if (page == null)
            {
                return false;
            }

            InfoPathPageDumper pageDumper = new InfoPathPageDumper();
            if (!pageDumper.DumpToPath(page, outputPath))
            {
                return false;
            }

            return true;

        }


        private InfoPathPage TranslateFormToPage(InfoPathForm form)
        {
            InfoPathPage page = new InfoPathPage();

            foreach (View v in form.XsnViews)
            {
                StringBuilder sb = new StringBuilder();
                foreach (Template t in v.Templates)
                {
                    t.GenerateJs(page.Action, form.XsnDataDictionary);
                    sb.Append(t.ToHtml());
                }
                page.HtmlMap.Add(v.Name, sb.ToString());
            }

            foreach (DomEventHandler eh in form.XsnManifest.EventHandlers)
            {
                this.GenerateJsForEventHandler(eh, form, page);
            }

            foreach (CustomValidation cv in form.XsnManifest.CustomValidations)
            {
                this.GenerateJsForCustomValidation(cv, form, page);
            }

            foreach (Caculation c in form.XsnManifest.Caculations)
            {
                this.GenerateJsForCaculation(c, form, page);
            }

            if (!this.GenerateJsForOnChange(page))
            {
                return null;
            }

            String initJs = this.GenerateInitJS(form, page);
            page.Action.InitFunctionJS = initJs;

            return page;
        }

        private Boolean GenerateJsForEventHandler(DomEventHandler eh, InfoPathForm form, InfoPathPage page)
        {
            RuleSet ruleSet = null;
            foreach (RuleSet rs in form.XsnManifest.RuleSets)
            {
                if (rs.Name.Equals(eh.RuleSetAction))
                {
                    ruleSet = rs;
                }
            }
            if (ruleSet == null)
            {
                return false;
            }
            foreach (Rule r in ruleSet.Rules)
            {
                foreach (Object ruleAction in r.RuleActions)
                {
                    ExpressionParser conditionParser = new ExpressionParser(r.Condition, eh.Match);
                    Expression conditionExp = conditionParser.Parse();
                    JSBlock conditionJs = conditionExp.GenerateExpJs(form.XsnDataDictionary);
                    conditionJs.JS = "    return (" + conditionJs.JS + ");";
                    String condtionId = page.Action.GenerateConditionId();
                    page.Action.SetConditionJs(condtionId, conditionJs);

                    if (ruleAction is RuleSetAssignmentAction)
                    {
                        RuleSetAssignmentAction a = (RuleSetAssignmentAction)ruleAction;
                        ExpressionParser expParser = new ExpressionParser(a.ExpressionText, eh.Match);
                        Expression exp = expParser.Parse();
                        JSBlock expJs = exp.GenerateExpJs(form.XsnDataDictionary);
                        expJs.JS = "    return (" + expJs.JS + ");";
                        String expConditionId = page.Action.GenerateConditionId();
                        page.Action.SetConditionJs(expConditionId, expJs);

                        String fieldName = a.TargetField;
                        int end = fieldName.LastIndexOf("/");
                        if (end >= 0)
                        {
                            fieldName = fieldName.Substring(end + 1);
                        }

                        StringBuilder sb = new StringBuilder();

                        sb.Append("    if (").Append(condtionId).Append("()").Append(")\r\n");
                        sb.Append("    {\r\n");
                        sb.Append("        setField(").Append("\"").Append(fieldName).Append("\",").Append(expConditionId).Append("());\r\n");
                        sb.Append("    }\r\n");

                        JSBlock assignJs = new JSBlock(sb.ToString());
                        assignJs.AppandReference(conditionJs);
                        assignJs.AppandReference(expJs);

                        String assignmentId = page.Action.GenerateAssignmentId();
                        page.Action.SetRuleActionJs(assignmentId, assignJs);
                    }
                    else if (ruleAction is RuleSetDialogBoxMessageAction)
                    {
                        RuleSetDialogBoxMessageAction dlgMsg = (RuleSetDialogBoxMessageAction)ruleAction;
                        StringBuilder sb = new StringBuilder();

                        sb.Append("    if (").Append(condtionId).Append("()").Append(")\r\n");
                        sb.Append("    {\r\n");
                        sb.Append("        alert(").Append("\"").Append(dlgMsg.Message.Replace("\"", "\\\"")).Append("\");\r\n");
                        sb.Append("    }\r\n");

                        JSBlock dlgMsgJs = new JSBlock(sb.ToString());
                        dlgMsgJs.AppandReference(conditionJs);

                        String dlgMsgId = page.Action.GenerateDialogMsgId();
                        page.Action.SetRuleActionJs(dlgMsgId, dlgMsgJs);
                    }
                    else if (ruleAction is RuleSetDialogBoxExpressionAction)
                    {
                        RuleSetDialogBoxExpressionAction dlgExp = (RuleSetDialogBoxExpressionAction)ruleAction;
                        ExpressionParser expParser = new ExpressionParser(dlgExp.ExpressionText, eh.Match);
                        Expression exp = expParser.Parse();
                        JSBlock expJs = exp.GenerateExpJs(form.XsnDataDictionary);
                        expJs.JS = "    return (" + expJs.JS + ");";
                        String expConditionId = page.Action.GenerateConditionId();
                        page.Action.SetConditionJs(expConditionId, expJs);

                        StringBuilder sb = new StringBuilder();

                        sb.Append("    if (").Append(condtionId).Append("()").Append(")\r\n");
                        sb.Append("    {\r\n");
                        sb.Append("        alert(").Append(expConditionId).Append("());\r\n");
                        sb.Append("    }\r\n");

                        JSBlock dlgExpJs = new JSBlock(sb.ToString());
                        dlgExpJs.AppandReference(conditionJs);
                        dlgExpJs.AppandReference(expJs);

                        String dlgExpId = page.Action.GenerateDialogExpId();
                        page.Action.SetRuleActionJs(dlgExpId, dlgExpJs);
                    }
                }
            }
            return true;
        }

        private Boolean GenerateJsForCustomValidation(CustomValidation cv, InfoPathForm form, InfoPathPage page)
        {
            String context = cv.Match;
            if (!cv.ExpressionContext.Equals("."))
            {
                context = context + "/" + cv.ExpressionContext;
            }
            ExpressionParser conditionParser = new ExpressionParser(cv.ExpressionText, context);
            Expression conditionExp = conditionParser.Parse();
            JSBlock conditionJs = conditionExp.GenerateExpJs(form.XsnDataDictionary);
            conditionJs.JS = "    return (" + conditionJs.JS + ");";
            String condtionId = page.Action.GenerateConditionId();
            page.Action.SetConditionJs(condtionId, conditionJs);

            StringBuilder sb = new StringBuilder();

            sb.Append("    if (").Append(condtionId).Append("()").Append(")\r\n");
            sb.Append("    {\r\n");
            if (cv.ErrorMsg.Type == CustomValidationErrorMsg.ErrorMsgType.modal)
            {
                sb.Append("        alert(\"").Append(cv.ErrorMsg.ShortMessage.Replace("\"", "\\\"")).Append("\");\r\n");
            }
            else if (cv.ErrorMsg.Type == CustomValidationErrorMsg.ErrorMsgType.modeless)
            {
                sb.Append("        alert(\"").Append(cv.ErrorMsg.ShortMessage.Replace("\"", "\\\"")).Append("\");\r\n");
            }
            sb.Append("    }\r\n");

            JSBlock validationJs = new JSBlock(sb.ToString());
            validationJs.AppandReference(conditionJs);

            String validationId = page.Action.GenerateValidationId();
            page.Action.SetValidationJs(validationId, validationJs);

            return true;
        }

        private Boolean GenerateJsForCaculation(Caculation c, InfoPathForm form, InfoPathPage page)
        {
            ExpressionParser expParser = new ExpressionParser(c.ExpressionText, c.Target);
            Expression exp = expParser.Parse();
            JSBlock expJs = exp.GenerateExpJs(form.XsnDataDictionary);
            expJs.JS = "    return (" + expJs.JS + ");";
            String expConditionId = page.Action.GenerateConditionId();
            page.Action.SetConditionJs(expConditionId, expJs);

            String fieldName = c.Target;
            int end = fieldName.LastIndexOf("/");
            if (end >= 0)
            {
                fieldName = fieldName.Substring(end + 1);
            }

            StringBuilder sb = new StringBuilder();

            sb.Append("    setField(\"").Append(fieldName).Append("\",").Append(expConditionId).Append("());");

            JSBlock caculationJs = new JSBlock(sb.ToString());
            caculationJs.AppandReference(expJs);

            String caculationId = page.Action.GenerateCaculationId();
            page.Action.SetCaculationJs(caculationId, caculationJs);
            return true;
        }

        private Boolean GenerateJsForOnChange(InfoPathPage page)
        {
            IDictionaryEnumerator i = page.Action.EvalueFieldJS.GetEnumerator();
            while (i.MoveNext())
            {
                String evalueFunName = (String)i.Key;
                HashSet<String> references = ((JSBlock)i.Value).ReferenceFields;
                foreach (String fieldName in references)
                {
                    String domId = (String)page.Action.FieldDomId[fieldName];
                    if (domId == null)
                    {
                        return false;
                    }
                    page.Action.AddOnChangeJS(domId, "eval_" + evalueFunName + "();");
                }
            }

            i = page.Action.RuleActionJS.GetEnumerator();
            while (i.MoveNext())
            {
                String funName = (String)i.Key;
                HashSet<String> references = ((JSBlock)i.Value).ReferenceFields;
                foreach (String fieldName in references)
                {
                    String domId = (String)page.Action.FieldDomId[fieldName];
                    if (domId == null)
                    {
                        return false;
                    }
                    page.Action.AddOnChangeJS(domId, funName + "();");
                }
            }

            i = page.Action.CaculationJS.GetEnumerator();
            while (i.MoveNext())
            {
                String funName = (String)i.Key;
                HashSet<String> references = ((JSBlock)i.Value).ReferenceFields;
                foreach (String fieldName in references)
                {
                    String domId = (String)page.Action.FieldDomId[fieldName];
                    if (domId == null)
                    {
                        return false;
                    }
                    page.Action.AddOnChangeJS(domId, funName + "();");
                }
            }

            i = page.Action.ValidationJS.GetEnumerator();
            while (i.MoveNext())
            {
                String funName = (String)i.Key;
                HashSet<String> references = ((JSBlock)i.Value).ReferenceFields;
                foreach (String fieldName in references)
                {
                    String domId = (String)page.Action.FieldDomId[fieldName];
                    if (domId == null)
                    {
                        return false;
                    }
                    page.Action.AddOnChangeJS(domId, funName + "();");
                }
            }
            return true;
        }



        private String GenerateInitJS(InfoPathForm form, InfoPathPage page)
        {
            DataDictionary data = form.XsnDataDictionary;
            ArrayList setDefaultValues = new ArrayList();
            IDictionaryEnumerator i = data.Fields.GetEnumerator();
            while (i.MoveNext())
            {
                String fieldName = (String)i.Key;
                Field fieldValue = (Field)i.Value;
                String js = "setField(\"" + fieldName + "\",\"" + fieldValue.GetStringValue() + "\");";
                setDefaultValues.Add(js);
            }
            setDefaultValues.Sort();

            ArrayList evals = new ArrayList();
            i = page.Action.EvalueFieldJS.GetEnumerator();
            while (i.MoveNext())
            {
                String evalDomId = (String)i.Key;
                evals.Add("eval_" + evalDomId + "();");
            }
            evals.Sort();

            ArrayList caculations = new ArrayList();
            i = page.Action.CaculationJS.GetEnumerator();
            while (i.MoveNext())
            {
                String caculationName = (String)i.Key;
                caculations.Add(caculationName + "();");
            }
            caculations.Sort();

            ArrayList actions = new ArrayList();
            i = page.Action.RuleActionJS.GetEnumerator();
            while (i.MoveNext())
            {
                String actionFunName = (String)i.Key;
                actions.Add(actionFunName + "();");
            }
            actions.Sort();


            StringBuilder sb = new StringBuilder();
            sb.Append("function init()\r\n");
            sb.Append("{\r\n");
            foreach (String s in setDefaultValues)
            {
                sb.Append("    ").Append(s).Append("\r\n");
            }
            foreach (String s in evals)
            {
                sb.Append("    ").Append(s).Append("\r\n");
            }
            foreach (String s in caculations)
            {
                sb.Append("    ").Append(s).Append("\r\n");
            }
            foreach (String s in actions)
            {
                sb.Append("    ").Append(s).Append("\r\n");
            }

            sb.Append("}\r\n");

            return sb.ToString();
        }
    }
}
